#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
 
#include "data_type.h"
#include "alloc.h"
#include "memfunc.h"
#include "basefunc.h"
#include "struct_deal.h"
#include "crypto_func.h"
#include "memdb.h"
#include "message.h"
#include "ex_module.h"
#include "sys_func.h"
#include "tcm_constants.h"
#include "tcm_structures.h"
#include "tcmfunc.h"
#include "tcm_cube_struct.h"
#include "tcm_key_manage.h"
#include "tcm_key_desc.h"
#include "tcm_pik_desc.h"
#include "key_certify.h"
// add para lib_include
   BYTE Buf[DIGEST_SIZE*16];
   RECORD(TCM_KEY_DESC,REMOTE_KEYSET) * remote_keyset_get_byname (char * name);
   RECORD(TCM_KEY_DESC,LOCAL_KEYSET) * local_keyset_get_byname (char * name);
int get_uuid_name(BYTE * uuid, char * name, char * path);

int key_certify_init(void * sub_proc, void * para)
{
	int ret;
	// Init Tcm Func
	// ret=TCM_LibInit();

	// add yorself's module init func here
	return 0;
}
int key_certify_start(void * sub_proc, void * para)
{
	int ret;
	void * recv_msg;
	int type;
	int subtype;
	// add yorself's module exec func here

	RECORD(MESSAGE,SIZED_BINDATA) * key_blob;
	MSG_EXPAND * expand;	

	while(1)
	{
		usleep(time_val.tv_usec);
		ret=ex_module_recvmsg(sub_proc,&recv_msg);
		if(ret<0)
			continue;
		if(recv_msg==NULL)
			continue;
			
		ret=message_get_define_expand(recv_msg,&expand,TYPE_PAIR(TCM_KEY_MANAGE,PRIVATE_KEY));
		if( expand!=NULL)
			proc_keycert_gen(sub_proc,recv_msg);
		else
		{
			ret=message_get_define_expand(recv_msg,&expand,TYPE_PAIR(TCM_PIK_DESC,KEYCERTIFY));
			if( expand!=NULL)
				proc_keycert_verify(sub_proc,recv_msg);
		}
//		if( message_get_flag(recv_msg)&MSG_FLAG_CRYPT)
//			proc_sessionkey_recover(sub_proc,recv_msg);
//		else
//			proc_sessionkey_gen(sub_proc,recv_msg);
	}
	return 0;
}

int proc_keycert_gen(void * sub_proc,void * recv_msg)
{
	int ret=0;

	BYTE local_uuid[DIGEST_SIZE];	
	BYTE user_name[DIGEST_SIZE];
	
	BYTE Namebuf[DIGEST_SIZE*4];
	BYTE nonce[DIGEST_SIZE];
	UINT32 result;
	RECORD(MESSAGE,SIZED_BINDATA) * key_blob;
	void * new_msg;

	MSG_EXPAND * msg_expand;
        RECORD(VTCM_IN_KEY,TCM_BIN_PUBKEY) * pubkey;
        RECORD(TCM_KEY_MANAGE,PRIVATE_KEY) * private_key;
        RECORD(TCM_KEY_DESC,LOCAL_KEYSET) * local_keyset;

	RECORD(TCM_PIK_DESC,KEYCERTIFY) * keycert;

	int key_len;
	int blob_len;
    	UINT32 authHandle;
    	UINT32 pikHandle;
    	UINT32 signkeyHandle;
   	BYTE SignBuf[256];
    	int  Buflen;
    	int SignLen;



        proc_share_data_getvalue("user_name",user_name);
        proc_share_data_getvalue("uuid",local_uuid);

        local_keyset=local_keyset_get_byname(user_name);
        if(local_keyset==NULL)
                return -EINVAL;

	printf("begin key load !\n");

	// get receiver's remote keyset
	ret=message_remove_expand(recv_msg,TYPE_PAIR(TCM_KEY_MANAGE,PRIVATE_KEY),&msg_expand);
	if(ret<0)
		return -EINVAL;
	if(msg_expand==NULL)
		return -EINVAL;
	private_key=msg_expand->expand;


        TCM_KEY * verifykey;
        TCM_KEY * verifiedkey;
        TCM_PUBKEY * verifiedpubkey;
        Memset(nonce,'A',DIGEST_SIZE);

        verifykey=malloc(sizeof(*verifykey));
        if(verifykey==NULL)
            return -EINVAL;

        verifiedkey=malloc(sizeof(*verifiedkey));
        if(verifiedkey==NULL)
            return -EINVAL;

        verifiedpubkey=malloc(sizeof(*verifiedpubkey));
        if(verifiedpubkey==NULL)
            return -EINVAL;

	get_uuid_name(local_keyset->pik_uuid,Namebuf,"tcmkey/");
   	ret = TCM_ExLoadTcmKey(verifykey, Namebuf);
   	 if(ret!=0)
    	{
        	printf("ExLoadTcmKey error!\n");
        	return -EINVAL;
    	}

	get_uuid_name(private_key->uuid,Namebuf,"tcmkey/");
    	ret = TCM_ExLoadTcmKey(verifiedkey, Namebuf);
    	if(ret!=0)
    	{
        	printf("ExLoadTcmKey error!\n");
        	return -EINVAL;
    	}	
        ret = TCM_ExGetPubkeyFromTcmkey(verifiedpubkey,verifiedkey);
    	if(ret!=0)
    	{
        	printf("ExGetPubKeyFromTcmKey error!\n");
        	return -EINVAL;
    	}	

    	ret=TCM_APCreate(TCM_ET_SMK, NULL, "sss", &authHandle);
    	printf("authHandle is : %x\n",authHandle);
    	if(ret<0)
    	{
        	printf("TCM_APCreate failed!\n");
        	return -EINVAL;
    	}
    	ret=TCM_LoadKey(0x40000000,authHandle,verifykey,&pikHandle);
    	if(ret<0)
    	{
        	printf("TCM_LoadKey failed!\n");
        	return -EINVAL;
    	}


	
	ret=TCM_LoadKey(0x40000000,authHandle,verifiedkey,&signkeyHandle);
    	if(ret<0)
    	{
        	printf("TCM_LoadKey failed!\n");
        	return -EINVAL;
    	}

    	ret=TCM_CertifyKey(pikHandle,signkeyHandle,nonce,
        	Buf,&Buflen,SignBuf,&SignLen);
    	if(ret<0)
    	{
        	printf("TCM_CertifyKey failed!\n");
        	return -EINVAL;
    	}

    	ret=TCM_APTerminate(authHandle);
    	if(ret<0)
    	{
        	printf("TCM_APTerminate %x failed!\n",authHandle);
        	return -EINVAL;
    	}

    	ret=TCM_EvictKey(pikHandle);
    	if(ret<0)
    	{
        	printf("TCM_EvictKey %x failed!\n",pikHandle);
        	return -EINVAL;
    	}

    	ret=TCM_EvictKey(signkeyHandle);
    	if(ret<0)
    	{
       	 	printf("TCM_EvictKey %x failed!\n",signkeyHandle);
        	return -EINVAL;
    	}

	// Output ekpub's desc and Data

//	new_msg=message_clone(recv_msg);

	keycert=Talloc0(sizeof(*keycert));
	if(keycert==NULL)
		return -ENOMEM;

	keycert->certify_data_size=Buflen;
	keycert->certify_data=Talloc0(Buflen);
	Memcpy(keycert->certify_data,Buf,Buflen);

	keycert->signLen=SignLen;
	keycert->signData=Talloc0(SignLen);
	Memcpy(keycert->signData,SignBuf,SignLen);

	Memcpy(keycert->nonce,nonce,DIGEST_SIZE);


	void * struct_template=memdb_get_template(TYPE_PAIR(VTCM_IN_KEY,TCM_BIN_PUBKEY));
	if(struct_template == NULL)
		return -EINVAL;
	
	struct_clone(verifiedpubkey,&keycert->pubkey,struct_template);
	Strncpy(keycert->userinfo.username,local_keyset->user_name,32);
	Memcpy(keycert->userinfo.node_uuid,local_uuid,DIGEST_SIZE);
		
	message_add_expand_data(recv_msg,TYPE_PAIR(TCM_PIK_DESC,KEYCERTIFY),keycert);
	
	ret=ex_module_sendmsg(sub_proc,recv_msg);
	return ret;
}
int proc_keycert_verify(void * sub_proc,void * recv_msg)
{
	int ret=0;

	BYTE local_uuid[DIGEST_SIZE];	
	
	BYTE Namebuf[DIGEST_SIZE*4];
	UINT32 result;

	RECORD(TCM_PIK_DESC,KEYCERTIFY) * key_cert;
	RECORD(TCM_KEY_DESC,VERIFY_DESC) verify_desc;

	MSG_EXPAND * msg_expand;
        RECORD(TCM_KEY_DESC,REMOTE_KEYSET) * remote_keyset;
    	TCM_PUBKEY * verifypubkey;
    	TCM_PUBKEY * verifiedpubkey;
	int blob_len;

	printf("begin keycert verify !\n");

	// remove 
	ret=message_remove_expand(recv_msg,TYPE_PAIR(TCM_PIK_DESC,KEYCERTIFY),&msg_expand);
	// get receiver's remote keyset
	if(ret<0)
		return -EINVAL;
	if(msg_expand==NULL)
		return -EINVAL;

	key_cert=msg_expand->expand;

	remote_keyset=remote_keyset_get_byname(key_cert->userinfo.username);
	if(remote_keyset==NULL)
		return -EINVAL;

    	verifypubkey=malloc(sizeof(*verifypubkey));
    	if(verifypubkey==NULL)
        	return -EINVAL;
    	verifiedpubkey=malloc(sizeof(*verifiedpubkey));
    	if(verifiedpubkey==NULL)
        	return -EINVAL;

	get_uuid_name(remote_keyset->pikpub_uuid,Namebuf,"pubkey/");
	ret=TCM_ExLoadTcmPubKey(verifypubkey,Namebuf);
	if(ret!=0)
	{
		printf("Load verifypubkey failed!\n");
		return ret;
	}	

	ret=TCM_ExSM2Verify(verifypubkey,key_cert->signData,key_cert->signLen,
		key_cert->certify_data,key_cert->certify_data_size);
    	printf("TCM_ExSM2Verify verify result is %d!\n",ret);
	verify_desc.result=ret;
	

	if(ret==0)
	{
    		TCM_CERTIFY_INFO certinfo;

    		ret=TCM_ExCertifyKeyVerify(&key_cert->pubkey,&certinfo,
			key_cert->certify_data,key_cert->certify_data_size);
    		printf("TCM_ExCertifyKeyVerify result is %d!\n",ret);
		verify_desc.result=ret;
	}
		

	blob_len=memdb_output_blob(key_cert,Buf,TYPE_PAIR(TCM_PIK_DESC,KEYCERTIFY));
	if(blob_len<0)
		return -EINVAL;
	vtcm_ex_sm3(verify_desc.object_uuid,1,Buf,blob_len);
	

	blob_len=memdb_output_blob(&key_cert->pubkey,Buf,TYPE_PAIR(VTCM_IN_KEY,TCM_BIN_PUBKEY));
	if(blob_len<0)
		return -EINVAL;
	vtcm_ex_sm3(remote_keyset->bindkey_uuid,1,Buf,blob_len);

	get_uuid_name(remote_keyset->bindkey_uuid,Namebuf,"pubkey/");
	
	ret=TCM_ExSaveTcmPubKey(&key_cert->pubkey,Namebuf);
	if(ret!=0)
		return -EINVAL;
	// add keydata in message
	ret=message_add_expand_data(recv_msg,TYPE_PAIR(TCM_PIK_DESC,VERIFYDATA),&verify_desc);
	if(ret<0)
		return -EINVAL;
	if(verify_desc.result==0)
	{
		ret=message_add_expand_data(recv_msg,TYPE_PAIR(TCM_KEY_DESC,REMOTE_KEYSET),remote_keyset);
		if(ret<0)
			return -EINVAL;
	}
	ret=ex_module_sendmsg(sub_proc,recv_msg);

	return ret;
}

RECORD(TCM_KEY_DESC,REMOTE_KEYSET) * remote_keyset_get_byname (char * name)
{
	int ret;
	RECORD(TCM_KEY_DESC,REMOTE_KEYSET ) * remote_keyset;
	DB_RECORD * db_record;
	char buf[DIGEST_SIZE];
	int i;
	Memset(buf,0,DIGEST_SIZE);
	Strncpy(buf,name,DIGEST_SIZE);
	
	db_record=memdb_find_first(TYPE_PAIR(TCM_KEY_DESC,REMOTE_KEYSET),"user_name",buf);

	if(db_record==NULL)
		return NULL;
	return db_record->record;

}
RECORD(TCM_KEY_DESC,LOCAL_KEYSET) * local_keyset_get_byname (char * name)
{
	int ret;
	RECORD(TCM_KEY_DESC,LOCAL_KEYSET ) * local_keyset;
	DB_RECORD * db_record;
	char buf[DIGEST_SIZE];
	Memset(buf,0,DIGEST_SIZE);
	Strncpy(buf,name,DIGEST_SIZE);
	db_record=memdb_find_first(TYPE_PAIR(TCM_KEY_DESC,LOCAL_KEYSET),"user_name",buf);

	if(db_record==NULL)
		return NULL;
	return db_record->record;
}

int get_uuid_name(BYTE * uuid, char * name, char * path)
{
	int ret=0;
	
	if(path!=NULL)
	{
		ret=Strlen(path);
		Memcpy(name,path,ret);
	}
	digest_to_uuid(uuid,name+ret);
	name[ret+DIGEST_SIZE*2]=0;
	return ret+DIGEST_SIZE;
}

